'''------------------------------------------------------------------------------------------------
 $ @Author: YSQ
 $ @Date: 2021-12-22 15:00:53
$ @LastEditTime: 2022-03-03 17:19:15
$ @LastEditors: your name
 $ @Description: evalToolKit 的父类
                -- 主要实现读取anno: 根据不同的数据集格式重载，默认输出的格式为 xywh
                -- 输出可视化跟踪结果: 两种不同的输出方式 -- img_file:    输出图像序列
                                                        -- video:       输出avi文件
                -- 加载基础参数
                -- 对跟踪器的性能进行汇总和分析，并输出:跟踪器的基本指标在tracker类中进行计算
 $ @FilePath: \YSQTrackerEvaluationToolKit_v1.0\EvalToolKit\evalToolKit.py
--------------------------------------------------------------------------------------------------'''
import sys
sys.path.append('./')
import numpy as np
import os
import openpyxl as opxl
import glob
import cv2

from prettytable import PrettyTable
from tqdm import tqdm
from matplotlib import pyplot as plt
from GetCfg.setParam import getParam
from GetCfg.getCfg import getYamlCfg
from EvalToolKit.tracker import Tracker
from Tools.draw import draw
from Tools.CalculateCurveIntegral import calculateCurveIntegral as cci
from Tools.MTree import MTree
from loguru import logger
from Tools.fileE_C import fileE_C


class EvalToolKit():
    def __init__(self,tracker_anno_dir:dict = None,output_dir:str = 'OutPut',useBbox:list = ['all']) -> None:
        '''-------------------------------------------------------------------------------------------
         * @description: 初始化 EvalToolKit class
         * @param {*} self
         * @param {dict} tracker_anno_dir=None
            -------------> type-dict -->['trackerName':'bbox_s dir']
            -------------> 一个字典存储跟踪器名与跟踪器bbox的键值对
         * @param {str}  output_dir='OutPut':
            ------------>输出结果的根目录默认为当前工程文件下的OutPut文件夹
         * @param {list} useBbox=None:
         -------------->type-dict->['trackerName1',...,'trackerNameN']
         * @return {*}
        ---------------------------------------------------------------------------------------------'''
        self.tracker = {}           # 存放tracker类的字典
        self.performanceACC = {}    # 存放跟踪器在各个挑战和OTB的表现-ACC
        self.performanceSUCC = {}   # 存放跟踪器在各个挑战和OTB的表现-SUCC
        self.gt = {}                # 存放gt的字典{'videoName':anno}
        self.args = getParam()      # EvalToolBKit 的基本参数
        self.YamlCfg = getYamlCfg(self.args.videosNameListDir)  # 读取Yaml文件中的参数
        self.color = [
            (0, 255, 255),
            (0, 255, 0),
            (255, 0, 0),
            (255, 0, 255),
            (255, 255, 0),
            (0, 0, 128),
            (0, 128, 0),
            (128, 0, 0),
            (0, 128, 128),
            (128, 0, 128),
            (128, 128, 0),
            (18,153,255),
            (3,97,255),
            (205,90,106),
            (15,94,56),
            (84,46,8),
            (96,48,176),
            (97,38,135),
            (18,74,115),
            ]
        # 性能评价指标图输出文件夹: TestPerformanceFigureDir
        self.TestPerformanceFigureDir = os.path.join(output_dir,'TestPerformance_Figure')
        # 表格输出文件夹: TabDir
        self.TabDir = os.path.join(output_dir,'Tab')
        #输出文件夹: analyzeTheTrackerOnEveryVideoDir
        self.analyzeTheTrackerOnEveryVideoDir = os.path.join(output_dir,'AnalyzeTheTrackerOnEveryVideo')
        # 可视化跟踪结果输出文件夹
        self.outputVisualizationResultsDir = os.path.join(output_dir,'outputVisualizationResults')
        # 测试日志输出文件夹
        self.logDir = os.path.join(output_dir,'log')
        #----------------------       确定需要评估的跟踪器anno       --------------------
        temp_tracker_box_dir = self.args.trackerBboxDir
        # 只评估 tracker_anno_dir 中的跟踪器
        if tracker_anno_dir != None and useBbox == []:
            self.args.trackerBboxDir = tracker_anno_dir
        # 评估 tracker_anno_dir中的跟踪器 与 useBox挑选的跟踪器
        elif tracker_anno_dir != None and useBbox != ['all']:
            self.args.trackerBboxDir = tracker_anno_dir
            for name in useBbox:
                try:
                    self.args.trackerBboxDir[name] = temp_tracker_box_dir[name]
                except:
                    print("工具箱未集成"+name+'的跟踪结果，请检查setParam.py文件')
        # 评估 tracker_anno_dir中的跟踪器 与 内置全部跟踪器
        elif tracker_anno_dir != None and useBbox[0] == 'all':
            for name in tracker_anno_dir.keys():
                self.args.trackerBboxDir[name] = tracker_anno_dir[name]
        # self.args 的属性
        #     |----- fps
        #     |----- OutputVisualizationResultsFormat
        #     |----- videosNameListDir
        #     |----- testSetDir
        #     |----- trackerBboxDir
        #     |----- starFrameAndEndFrame
        #     |----- CalculatingIndexMethod
        #     |----- failureFrameNumThreshold
        #     |----- failureIOUThreshold
        #     |----- challenge
        # self.YamlCfg 的属性
        #    |----- videosNameList = []
        #    |----- outputVideosList = []
        #    |----- IV = []
        #    |----- SV = []
        #    |----- OCC = []
        #    |----- DEF = []
        #    |----- MB = []
        #    |----- FM = []
        #    |----- IPR = []
        #    |----- OPR = []
        #    |----- OV = []
        #    |----- BC = []
        #    |----- LR = []
        d = 0

    def init(self):
        '''-------------------------------------------------------------------------------------------
        * @description: 初始化toolkit;
                        -公有成员函数，对外开放
                        -主要实现的功能为:
                        |-----> 读取跟踪器的跟踪结果
                        |-----> 读取ground truth
                        |-----> 初始化所有跟踪器类
                        |-----> 在每个跟踪器类中计算他们自己的IOU ERR SUCC ACC
                        |-----> 检查输出文件夹是否存在，不存在则创建它
        * @param {*} self
        * @return {*}
        ---------------------------------------------------------------------------------------------'''
        print(MTree(0,'初始化 ToolKit'))
        # -read anno
        # ---- read ground truth
        for name in tqdm(self.YamlCfg.videosNameList,desc=MTree(1,'Load Ground Truth')):
            if name in self.args.starFrameAndEndFrame.keys():
                if self.args.starFrameAndEndFrame[name][1] == 0:
                    self.gt[name] = self.loadAnno(self.args.testSetDir+'/'+name+'/groundtruth_rect.txt')[
                        self.args.starFrameAndEndFrame[name][0]:]
                else:
                    self.gt[name] = self.loadAnno(self.args.testSetDir+'/'+name+'/groundtruth_rect.txt')[
                        self.args.starFrameAndEndFrame[name][0]:self.args.starFrameAndEndFrame[name][1]]
            self.gt[name] = self.loadAnno(self.args.testSetDir+'/'+name+'/groundtruth_rect.txt')
        # ---- 读每个跟踪器的跟踪结果，并初始化每个跟踪器的 tracker 类
        colorIdx = 0
        for tracker in self.args.trackerBboxDir.keys():
            tempAnnos = {}
            for i in tqdm(range(len(self.YamlCfg.videosNameList)),desc = MTree(1,'Load '+tracker+' Bbox')):
                name = self.YamlCfg.videosNameList[i]
                if name in self.args.starFrameAndEndFrame.keys():
                    if self.args.starFrameAndEndFrame[name][1] == 0:
                        tempAnnos[name] = self.loadAnno(self.args.trackerBboxDir[tracker]+'/'+name+'.txt')[
                            self.args.starFrameAndEndFrame[name][0]:]
                    else:
                        tempAnnos[name] = self.loadAnno(self.args.trackerBboxDir[tracker]+'/'+name+'.txt')[
                            self.args.starFrameAndEndFrame[name][0]:self.args.starFrameAndEndFrame[name][1]]
                tempAnnos[name] = self.loadAnno(self.args.trackerBboxDir[tracker]+'/'+name+'.txt')
            self.tracker[tracker] = Tracker(tracker,self.args.trackerBboxDir[tracker],self.color[colorIdx],tempAnnos)
            colorIdx += 1
        # -- 计算每个跟踪器的相关指标
        print(MTree(0,'Calculation Performance'))
        for tracker in self.args.trackerBboxDir.keys():
            self.tracker[tracker].getERR(self.gt)
            self.tracker[tracker].getIOU(self.gt)
            self.tracker[tracker].getACC()
            self.tracker[tracker].getSUCC()
            self.tracker[tracker].getFailureVideos(self.args.failureIOUThreshold,
                                                self.args.failureFrameNumThreshold,
                                                self.args.starFrameAndEndFrame)
        # -- 检查输出文件夹是否存在，不存在则创建它
        self._checkOutputFolder()

    def testPerformance(self):
        '''-------------------------------------------------------------------------------------------
        * @description: 可视化所有跟踪器在不同挑战下的性能: 性能计算在tracker类中完成
                        -将每个跟踪器在每个视频上的性能存储到 .xlsx文件中
                        -公有成员函数，对外开放
        * @param {*} self
        * @return {*}
        ---------------------------------------------------------------------------------------------'''
        print(MTree(0,'Test Performance -- Draw Performance\'s Fig'))
        self._showOTBAns()
        self._showChallengeAns('IV',self.YamlCfg.IV)
        self._showChallengeAns('SV',self.YamlCfg.SV)
        self._showChallengeAns('OCC',self.YamlCfg.OCC)
        self._showChallengeAns('DEF',self.YamlCfg.DEF)
        self._showChallengeAns('MB',self.YamlCfg.MB)
        self._showChallengeAns('FM',self.YamlCfg.FM)
        self._showChallengeAns('IPR',self.YamlCfg.IPR)
        self._showChallengeAns('OPR',self.YamlCfg.OPR)
        self._showChallengeAns('OV',self.YamlCfg.OV)
        self._showChallengeAns('BC',self.YamlCfg.BC)
        self._showChallengeAns('LR',self.YamlCfg.LR)
        self._saveVideoSuccAccXlsx()

    '''-------------------------------------------------------------------------------------------
     * @description: 对跟踪器进行逐帧分析:每一帧的误差，IOU,是否失败，并存储失败帧
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def analyze(self):
        print(MTree(0,'Analyze The Tracker On Every Video'))
        self._analyzeTheTrackerOnEveryVideo()
        self._saveTrackerFailureFrames()

    '''-------------------------------------------------------------------------------------------
     * @description: 集成输出所有跟踪器的可视化跟踪结果
                    -可选择输出为 imgFile 或 video
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def outputVisualizationResults(self):
        MTree(0,'outputVisualizationResults')
        if self.args.OutputVisualizationResultsFormat == 'imgFile':
            self._outputVisualizationResults_imgFile()
        elif self.args.OutputVisualizationResultsFormat == 'video':
            self._outputVisualizationResults_video(self.args.fps)
        elif self.args.OutputVisualizationResultsFormat == 'B':
            self._outputVisualizationResults_imgFile()
            self._outputVisualizationResults_video(self.args.fps)
        elif self.args.OutputVisualizationResultsFormat == 'N':
            return

    '''-------------------------------------------------------------------------------------------
     * @description: 绘制所有跟踪器在该视频上的误差/IOU曲线,并输出每个跟踪器在该视频上每一帧的误差/IOU文件
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def _analyzeTheTrackerOnEveryVideo(self):
        for videoName in tqdm(self.YamlCfg.videosNameList,MTree(1,'Analyze Every Video')):
            vDir = self.analyzeTheTrackerOnEveryVideoDir+'/'+videoName
            fileE_C(vDir)
            # 绘制所有跟踪器在该视频上的误差曲线,并输出每个跟踪器在该视频上每一帧的误差文件
            for trackerName in self.tracker.keys():
                draw('ERR','OTB',self.tracker[trackerName].ERR[videoName],trackerName,videoName=videoName)
                np.savetxt(vDir+'/'+trackerName+'ERR.txt',self.tracker[trackerName].ERR[videoName])
            plt.legend()
            plt.savefig(vDir+'/ERR.jpg')
            plt.close()
            # 绘制所有跟踪器在该视频上的IOU曲线，并输出每个跟踪器在该视频上每一帧的IOU文件
            for trackerName in self.tracker.keys():
                draw('IOU','OTB',self.tracker[trackerName].IOU[videoName],trackerName,videoName=videoName)
                np.savetxt(vDir+'/'+trackerName+'IOU.txt',self.tracker[trackerName].IOU[videoName])
            plt.legend()
            plt.savefig(vDir+'/IUO.jpg')
            plt.close()

    '''-------------------------------------------------------------------------------------------
     * @description: 保存每个跟踪器的跟踪失败帧到 xlsx文件中
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def _saveTrackerFailureFrames(self):
        for trackerName in self.tracker.keys():
            book = opxl.Workbook()
            sheet = book.active
            idx = 0
            for video in self.tracker[trackerName].failureFrame.keys():
                idx += 1
                sheet.cell(1,idx,value=video)
                for frame in range(len(self.tracker[trackerName].failureFrame[video])):
                    sheet.cell(frame+2,idx,value=self.tracker[trackerName].failureFrame[video][frame])
            book.save(self.TabDir+'/'+trackerName+'FailureFrames.xlsx')

    '''-------------------------------------------------------------------------------------------
     * @description: 保存每个跟踪器在所有视频上的精度指标，方便后续分析跟踪器
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def _saveVideoSuccAccXlsx(self):
        bookAcc = opxl.Workbook()
        bookSucc = opxl.Workbook()
        sheetAcc = bookAcc.active
        sheetSucc = bookSucc.active

        for i in range(len(self.YamlCfg.videosNameList)):
            sheetAcc.cell(i+2,1,value=self.YamlCfg.videosNameList[i])
            sheetSucc.cell(i+2,1,value=self.YamlCfg.videosNameList[i])
        col = 1
        for tracker in self.tracker.keys():
            col += 1
            sheetAcc.cell(1,col,value=tracker)
            sheetSucc.cell(1,col,value=tracker)
            for j in range(len(self.YamlCfg.videosNameList)):
                videoName = self.YamlCfg.videosNameList[j]
                if self.args.CalculatingIndexMethod["ACC"] == 'Threshold':
                    sheetAcc.cell(j+2,col,value=self.tracker[tracker].ACC[videoName][20])
                if self.args.CalculatingIndexMethod["ACC"] == 'Integral':
                    sheetAcc.cell(j+2,col,value=cci(self.tracker[tracker].ACC[videoName]))
                if self.args.CalculatingIndexMethod["SUCC"] == 'Threshold':
                    sheetSucc.cell(j+2,col,value=self.tracker[tracker].SUCC[videoName][50])
                if self.args.CalculatingIndexMethod["SUCC"] == 'Integral':
                    sheetSucc.cell(j+2,col,value=cci(self.tracker[tracker].SUCC[videoName]))
        bookAcc.save(self.TabDir+'/acc.xlsx')
        bookSucc.save(self.TabDir+'/succ.xlsx')

    '''-------------------------------------------------------------------------------------------
     * @description: 绘制每个跟踪器在OTB数据集下的曲线图
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def _showOTBAns(self):
        pA = {}
        pS = {}
        tempP = []
        tempIdx = {}
        realIdxLenged = []
        for tracker in self.tracker.keys():
            pA[tracker] = draw('ACC','OTB',self.tracker[tracker].ACC['all'],tracker,
            self.args.CalculatingIndexMethod['ACC'])
            tempP.append(pA[tracker])
        # 对跟踪器性能进行排序
        for i in range(len(tempP)):
            tempIdx[tempP[i]]=i
        tempP.sort(reverse=True)
        for i in tempP:
            realIdxLenged.append(tempIdx[i])
        # 对lenged的顺序进行调整
        handlesT,lablesT = plt.gca().get_legend_handles_labels()
        handles,lables = plt.gca().get_legend_handles_labels()
        for i in range(len(realIdxLenged)):
            handles[i]=handlesT[realIdxLenged[i]]
            lables[i]=lablesT[realIdxLenged[i]]
        # 绘制图例
        plt.legend(handles,lables)
        plt.savefig(self.TestPerformanceFigureDir+'/acc_OTB.jpg')
        plt.close()
        self.performanceACC['OTB'] = pA

        tempP = []
        tempIdx = {}
        realIdxLenged = []
        for tracker in self.tracker.keys():
            pS[tracker] = draw('SUCC','OTB',self.tracker[tracker].SUCC['all'],tracker,
            self.args.CalculatingIndexMethod['SUCC'])
            tempP.append(pS[tracker])
        # 对跟踪器性能进行排序
        for i in range(len(tempP)):
            tempIdx[tempP[i]]=i
        tempP.sort(reverse=True)
        for i in tempP:
            realIdxLenged.append(tempIdx[i])
        # 对lenged的顺序进行调整
        handlesT,lablesT = plt.gca().get_legend_handles_labels()
        handles,lables = plt.gca().get_legend_handles_labels()
        for i in range(len(realIdxLenged)):
            handles[i]=handlesT[realIdxLenged[i]]
            lables[i]=lablesT[realIdxLenged[i]]
        # 绘制图例
        plt.legend(handles,lables)
        plt.savefig(self.TestPerformanceFigureDir+'/succ_OTB.jpg')
        plt.close()
        self.performanceSUCC['OTB'] = pS

    '''-------------------------------------------------------------------------------------------
     * @description: 绘制每个跟踪器在OTB数据集不同挑战下的曲线图
     * @param {*} self
     * @param {*} challenge
     * @param {*} challangeList
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def _showChallengeAns(self,challenge, challangeList):
        pA = {}
        pS = {}
        tempP = []
        tempIdx = {}
        realIdxLenged = []

        for tracker in self.tracker.keys():
            vNum = 0
            temp = np.zeros(50)
            for video in challangeList:
                if video in self.YamlCfg.videosNameList:
                    vNum += 1
                    temp += self.tracker[tracker].ACC[video]
                else:
                    pass
            if vNum != 0:
                temp /= vNum
                pA[tracker] = draw('ACC',challenge,temp,tracker,self.args.CalculatingIndexMethod['ACC'])
                tempP.append(pA[tracker])
            else:
                return
         # 对跟踪器性能进行排序
        for i in range(len(tempP)):
            tempIdx[tempP[i]]=i
        tempP.sort(reverse=True)
        for i in tempP:
            realIdxLenged.append(tempIdx[i])
        # 对lenged的顺序进行调整
        handlesT,lablesT = plt.gca().get_legend_handles_labels()
        handles,lables = plt.gca().get_legend_handles_labels()
        for i in range(len(realIdxLenged)):
            handles[i]=handlesT[realIdxLenged[i]]
            lables[i]=lablesT[realIdxLenged[i]]
        # 绘制图例
        plt.legend(handles,lables)
        plt.savefig(self.TestPerformanceFigureDir+'/succ_'+challenge+'.jpg')
        plt.close()
        self.performanceACC[challenge] = pA

        tempP = []
        tempIdx = {}
        realIdxLenged = []
        for tracker in self.tracker.keys():
            vNum = 0
            temp = np.zeros(100)
            for video in challangeList:
                if video in self.YamlCfg.videosNameList:
                    vNum += 1
                    temp += self.tracker[tracker].SUCC[video]
                else:
                    pass
            if vNum != 0:
                temp /= vNum
                pS[tracker] = draw('SUCC',challenge,temp,tracker,self.args.CalculatingIndexMethod['SUCC'])
                tempP.append(pS[tracker])
            else:
                return
        # 对跟踪器性能进行排序
        for i in range(len(tempP)):
            tempIdx[tempP[i]]=i
        tempP.sort(reverse=True)
        for i in tempP:
            realIdxLenged.append(tempIdx[i])
        # 对lenged的顺序进行调整
        handlesT,lablesT = plt.gca().get_legend_handles_labels()
        handles,lables = plt.gca().get_legend_handles_labels()
        for i in range(len(realIdxLenged)):
            handles[i]=handlesT[realIdxLenged[i]]
            lables[i]=lablesT[realIdxLenged[i]]
        # 绘制图例
        plt.legend(handles,lables)
        plt.savefig(self.TestPerformanceFigureDir+'/acc_'+challenge+'.jpg')
        plt.close()
        self.performanceSUCC[challenge] = pS

    '''-------------------------------------------------------------------------------------------
     * @description: 输出toolkit 的评估日志
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def outputLog(self):
        logger.add(self.logDir+'/toolkitEvalLog_{time}.log',format="{message}",level = "INFO")
        logger.info('------------     参数设置     -------------')
        logger.info(MTree(0,'跟踪挑战集合'))
        logger.info(MTree(1,'OTB - '+self.args.challenge['OTB']))
        logger.info(MTree(2,' ')+str(self.YamlCfg.videosNameList))


        logger.info(MTree(1,'IV - '+self.args.challenge['IV']))
        logger.info(MTree(2,' ')+str(self.YamlCfg.IV))


        logger.info(MTree(1,'SV - '+self.args.challenge['SV']))
        logger.info(MTree(2,' ')+ str(self.YamlCfg.SV) )


        logger.info(MTree(1,'OCC - '+self.args.challenge['OCC']))
        logger.info(MTree(2," ") +str(self.YamlCfg.OCC) )


        logger.info(MTree(1,'DEF - '+self.args.challenge['DEF']))
        logger.info(MTree(2," ") +str(self.YamlCfg.DEF) )

        logger.info(MTree(1,'MB - '+self.args.challenge['MB']))
        logger.info(MTree(2," ") +str(self.YamlCfg.MB) )


        logger.info(MTree(1,'FM - '+self.args.challenge['FM']))
        logger.info(MTree(2," ") +str(self.YamlCfg.FM) )

        logger.info(MTree(1,'IPR - '+self.args.challenge['IPR']))
        logger.info(MTree(2," ") +str(self.YamlCfg.IPR) )

        logger.info(MTree(1,'OPR - '+self.args.challenge['OPR']))
        logger.info(MTree(2," ") +str(self.YamlCfg.OPR) )

        logger.info(MTree(1,'OV - '+self.args.challenge['OV']))
        logger.info(MTree(2," ") +str(self.YamlCfg.IV) )

        logger.info(MTree(1,'BC - '+self.args.challenge['BC']))
        logger.info(MTree(2," ") +str(self.YamlCfg.BC) )

        logger.info(MTree(1,'LR - '+self.args.challenge['LR']))
        logger.info(MTree(2," ") +str(self.YamlCfg.LR) )

        logger.info(MTree(0,'Special video start frame and end frame'))
        for name in self.args.starFrameAndEndFrame.keys():
            logger.info(MTree(1,name + ': ')+str(self.args.starFrameAndEndFrame[name]) )

        logger.info(MTree(0,'The evaluated tracker and its corresponding tracking result storage address'))
        for name in self.args.trackerBboxDir.keys():
            logger.info(MTree(1,name + ': '+self.args.trackerBboxDir[name]))

        logger.info(MTree(0,'The address of the OTB data set: '+self.args.testSetDir))
        logger.info(MTree(0,'Threshold for determining the number of frames of each video as a failed video: '+str(self.args.failureFrameNumThreshold)))
        logger.info(MTree(0,'The method of calculating the indicator, if the card threshold method is used: Threshold, the curve integral is calculated: Integral: ACC->'
            +self.args.CalculatingIndexMethod['ACC']+'\tSUCC->'+self.args.CalculatingIndexMethod['SUCC']))
        logger.info(MTree(0,'The IOU threshold of the frame that is judged to be a failed frame: '+str(self.args.failureIOUThreshold)))

        logger.info('----------------------------------------------------------------')
        logger.info('----------------------------------------------------------------')
        logger.info('------                   评估与分析                        ------')
        logger.info('----------------------------------------------------------------')
        logger.info('----------------------------------------------------------------')

        logger.info(MTree(0,'跟踪器存在跟踪失败的视频: '))
        table_fv = PrettyTable(['tracker','nums of failed videos'])
        for tracker in self.tracker.keys():
            names = []
            for name in self.tracker[tracker].failureFrame.keys():
                names.append(name)
            logger.info(MTree(1,tracker))
            logger.info(MTree(2,str(names)))
            table_fv.add_row([tracker,str(len(names))])
        logger.info('\n'+str(table_fv)+'\n')



        for ch in self.args.challenge.keys():
            self._printPerformanceACC(ch,'ACC')
            self._printPerformanceACC(ch,'SUCC')

    def _printPerformanceACC(self,ch,AS):
        logger.info('------------    Performance On '+ch+' —— '+AS+'    ------------')
        logger.info('-- '+ch+': '+self.args.challenge[ch])
        logger.info('-- Index calculation method:\t'+self.args.CalculatingIndexMethod[AS])

        if AS == 'ACC':
            temp = sorted(self.performanceACC[ch].items(),key=lambda item:item[1],reverse=True)
        elif AS == 'SUCC':
            temp = sorted(self.performanceSUCC[ch].items(),key=lambda item:item[1],reverse=True)
        table = PrettyTable(['tracker',AS])
        for p in temp:
            table.add_row([p[0],p[1]])

        logger.info('\n'+str(table)+'\n')

    '''-------------------------------------------------------------------------------------------
     * @description: 检查输出文件夹是否存在，不存在则创建它
     * @param {*} self
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def _checkOutputFolder(self):
        fileE_C(self.analyzeTheTrackerOnEveryVideoDir)
        fileE_C(self.TabDir)
        fileE_C(self.TestPerformanceFigureDir)
        fileE_C(self.outputVisualizationResultsDir)
        fileE_C(self.logDir)

    def _outputVisualizationResults_imgFile(self):
        for name in tqdm(self.YamlCfg.outputVideosList,desc=MTree(1,'output_imgFile')):
            vedio_dir = self.args.testSetDir+'/'+name
            seq_dir = os.path.expanduser(vedio_dir)
            os.path.join(seq_dir, 'img')
            img_files = sorted(glob.glob(os.path.join(seq_dir, 'img/') + '*.jpg'))

            if name in self.args.starFrameAndEndFrame.keys():
                if self.args.starFrameAndEndFrame[name][1] == 0:
                    img_files = img_files[self.args.starFrameAndEndFrame[name][0]:]
                else:
                    img_files = img_files[self.args.starFrameAndEndFrame[name][0]:
                                            self.args.starFrameAndEndFrame[name][1]]
            vedioSaveDir = self.outputVisualizationResultsDir+'/'+name
            if not os.path.exists(vedioSaveDir):
                os.makedirs(vedioSaveDir)
            i = 0
            for idx in img_files:
                img = cv2.imread(idx)
                j = 2
                for tracker in self.tracker.keys():
                    try:
                        box = self.tracker[tracker].anno[name][i]
                    except:
                        pass
                    box = np.array(box, dtype=np.int)
                    pt1 = (box[0], box[1])
                    pt2 = (box[0] + box[2], box[1] + box[3])
                    img = cv2.rectangle(img, pt1, pt2, self.tracker[tracker].color, thickness=2)
                    if name in self.args.starFrameAndEndFrame.keys():
                        frame_idx = i+self.args.starFrameAndEndFrame[name][0]+1
                    else:
                        frame_idx = i+1
                    cv2.putText(img, "frame:%08d"%frame_idx, (20, 1 * 20), cv2.FONT_HERSHEY_COMPLEX, 0.7, self.tracker[tracker].color,
                                1)
                    cv2.putText(img, tracker, (20, j * 20), cv2.FONT_HERSHEY_COMPLEX, 0.7, self.tracker[tracker].color,
                                1)
                    j +=1
                # -----------  画 gt -------------
                box = self.gt[name][i]
                box = np.array(box, dtype=np.int)
                pt1 = (box[0], box[1])
                pt2 = (box[0] + box[2], box[1] + box[3])
                img = cv2.rectangle(img, pt1, pt2, (0,0,255), thickness=2)
                cv2.putText(img, 'groundtruth', (20, j * 20), cv2.FONT_HERSHEY_COMPLEX, 0.7, (0,0,255),1)
                i += 1
                cv2.imwrite(vedioSaveDir+'/'+'%08d'%frame_idx+'.jpg',img)

    def _outputVisualizationResults_video(self,fps):
        for name in tqdm(self.YamlCfg.outputVideosList,desc=MTree(1,'output video')):
            vedio_dir = self.args.testSetDir + '/'+name
            seq_dir = os.path.expanduser(vedio_dir)
            os.path.join(seq_dir, 'img')
            img_files = sorted(glob.glob(os.path.join(seq_dir, 'img/') + '*.jpg'))

            if name in self.args.starFrameAndEndFrame.keys():
                if self.args.starFrameAndEndFrame[name][1] == 0:
                    img_files = img_files[self.args.starFrameAndEndFrame[name][0]:]
                else:
                    img_files = img_files[self.args.starFrameAndEndFrame[name][0]:
                                            self.args.starFrameAndEndFrame[name][1]]

            frame_num = len(img_files)
            img_temp = cv2.imread(img_files[0])
            w,h = img_temp.shape[0],img_temp.shape[1]
            fourcc = cv2.VideoWriter_fourcc(*'XVID')
            videoWriter = cv2.VideoWriter(self.outputVisualizationResultsDir+'/'+name+'.avi',
                                            fourcc,fps,(h,w))

            i = 0
            for idx in img_files:
                img = cv2.imread(idx)
                j = 2
                for tracker in self.tracker.keys():
                    box = self.tracker[tracker].anno[name][i]
                    box = np.array(box, dtype=np.int)
                    pt1 = (box[0], box[1])
                    pt2 = (box[0] + box[2], box[1] + box[3])
                    img = cv2.rectangle(img, pt1, pt2, self.tracker[tracker].color, thickness=2)
                    cv2.putText(img,"frame:%08d"%i,(20, 1 * 20),cv2.FONT_HERSHEY_COMPLEX, 0.7, self.tracker[tracker].color,
                                1)
                    cv2.putText(img, tracker, (20, j * 20), cv2.FONT_HERSHEY_COMPLEX, 0.7, self.tracker[tracker].color,
                                1)
                    j +=1
                # -----------  画 gt -------------
                box = self.gt[name][i]
                box = np.array(box, dtype=np.int)
                pt1 = (box[0], box[1])
                pt2 = (box[0] + box[2], box[1] + box[3])
                img = cv2.rectangle(img, pt1, pt2, (0,0,255), thickness=2)
                cv2.putText(img, 'groundtruth', (20, j * 20), cv2.FONT_HERSHEY_COMPLEX, 0.7, (0,0,255),1)
                i += 1
                videoWriter.write(img)


    '''-------------------------------------------------------------------------------------------
     * @description:  读取anno文件，且文件format为: xywh，输出一个np.arry 格式为[x,y,w,h]
                    #!对于不同的 anno 格式输入与输出请重载此函数，
     * @param {*} self
     * @param {*} dir --> 文件地址
     * @return {*}
    ---------------------------------------------------------------------------------------------'''
    def loadAnno(self,dir:str):
        try:
            anno = np.loadtxt(dir, delimiter=',')  # 加载所有的ground truth
        except:
            anno = np.loadtxt(dir)
        return anno



if __name__ == '__main__':
    exp = EvalToolKit()
    exp.init()
    exp.testPerformance()
    exp.analyze()
    exp.outputLog()
